<template xmlns="http://www.w3.org/1999/html">
  <div @click="inputRef.click()">
    <input type="file" ref="inputRef" style="display: none" :multiple="props.multiple"
           :name="props.name" :accept="props.accept" @change="onChange">
    <slot :loading="loading">
      <el-button size="small" type="primary" :loading="loading">点击上传</el-button>
    </slot>
  </div>
</template>
<script setup lang="ts">
/* eslint-disable */
import Layers from "@/lang/layers/index";
import {ref} from "vue";

/**
 * 文件上传
 *
 * 这个不是标准的表单组件，没有 v-model 绑定值。
 *
 * 对于文件上传功能，这个组件实际非常实用。
 *
 * 之前想过很多奇奇怪怪的封装，既费时又不实用。
 * 这个组件足够简单，逻辑非常简单，可以很方便地进行各种二次封装。
 */
// 定义 props 接口
const props = defineProps({
    // 字段名
    name: {type: String}
    // 可接受的文件类型
    , accept: {type: String, default: '*'}
    // 是否允许多选
    , multiple : {type: Boolean, default: false}
    // 文件上传函数
    , upload: {type: Function, default: undefined}
    // 文件列表，单向传递参数
    , fileList: {type: Function, default: Layers.array}
    // 所有文件完成提交之后，触发当前事件
    , change: {type: Function, default: undefined}
});

// Reference
const inputRef = ref()

// 是否正在加载
const loading = ref(false);

/**
 * 用户选择了新的文件
 *
 * @param event change事件对象实例
 */
const onChange = (event) => {
    loading.value = true;
    let files = event.target.files;
    try {
        // 链式调用上传函数，一个传完传下一个；
        // 改成 all() 调用，也有一定道理，可以提升效率。
        let ret = Layers.sequentialPromise((resolve, i) => {
            props.upload(files[i]).then(item => {
                props.fileList.push(item);
                resolve();
            })
        }, 0, files.length);

        ret.then(() => {
            Layers.executeFunction(props.change, props.fileList);
            // 完成所有文件的提交，关闭加载条
            loading.value = false
            event.target.value = '';
        });
    } catch (e) {
        console.error(e);
        // 触发异常，关闭加载条
        loading.value = false
        event.target.value = '';
    }
}
</script>